/*
 * ShaderTypes.cpp
 *
 *  Created on: 1.10.2011
 *      Author: Blinov Kirill
 *      Copyright: LGPL
 */

#include "ShaderTypes.h"
#include "../Constants.h"

CShaderProgram::CShaderProgram( const GLuint ShaderName ) :
    programName(ShaderName)
{
}

CShaderProgram::~CShaderProgram()
{
	glDeleteProgram( programName );
}

bool
CShaderProgram::InitShader( const GLuint ProgramName )
{
	//TODO May be we should compile shader here?
	bool result = ( 0 == programName );
	if ( result )
	{
		programName = ProgramName;
	}
	return result;
}

GLint
CShaderProgram::GetShaderParamReference( const std::string ParameterName )
{
	return glGetUniformLocation( programName, ParameterName.c_str() );
}

void
CShaderProgram::AddParamContainer( IShaderParamContainer* Container )
{
	containers.push_back( Container );
}

void
CShaderProgram::SetParam( SHADER_COMMON_PARAMS &Params )
{
	tmpParamPointer = &Params;

	for ( std::vector<IShaderParamContainer*>::const_iterator BeginIt = containers.begin(),
          FinishIt = containers.end();
          BeginIt != FinishIt; ++BeginIt )
	{
		IShaderParamContainer* CurrentContainer = *BeginIt.base();
		CurrentContainer->SetParam( this );
	}
}

void
CShaderProgram::SetParam( const GLint ParamName, const UINT Value )
{
	glUniform1i( ParamName, Value );
}

void
CShaderProgram::SetParam( const GLint ParamName, const GLint Value )
{
	glUniform1i( ParamName, Value );
}

void
CShaderProgram::SetParam( const GLint ParamName, const float Value )
{
	glUniform1f( ParamName, Value );
}

void
CShaderProgram::SetParam( const GLint ParamName, const glm::vec3& Value )
{
	glUniform3fv( ParamName, 1, glm::value_ptr( Value ) );
}

void
CShaderProgram::SetParam( const GLint ParamName, const glm::vec4& Value )
{
	glUniform4fv( ParamName, 1, glm::value_ptr( Value ) );
}

void
CShaderProgram::SetParam( const GLint ParamName, const glm::mat4& Value )
{
	glUniformMatrix4fv( ParamName, 1, GL_FALSE, glm::value_ptr( Value ) );
}

void
CShaderProgram::SetParam( const GLint ParamName, const std::vector<glm::mat4>& Value )
{
	glUniformMatrix4fv( ParamName, Value.size(), GL_FALSE, glm::value_ptr( Value[0] ) );
}

void
CShaderProgram::SetParam( const GLint ParamName, const TEXTURE_DESCRIPTOR& Value )
{
	SetTextureToCurrentBlock( ParamName, Value.textureGLid, Value.textureType );
}

void
CShaderProgram::SetParam( const GLint ParamName, const SHADER_INPUT_COMMON_VALUES Value )
{
	switch( Value )
	{
		break;case SHADER_INPUT_MVP_MATRIX:
		{
			glm::mat4 MVP = tmpParamPointer->ProjectionMatrix *
							tmpParamPointer->ViewMatrix *
					        tmpParamPointer->ModelMatrix;
			glUniformMatrix4fv( ParamName, 1, GL_FALSE, glm::value_ptr( MVP ) );
		};
		break;case SHADER_INPUT_MODEL_MATRIX:
		{
			glUniformMatrix4fv( ParamName, 1, GL_FALSE, glm::value_ptr( tmpParamPointer->ModelMatrix ) );
		};
		break;case SHADER_INPUT_BONES_ARRAY_32:
		{
			//TODO Add support for bones animation
		};
		break;case SHADER_INPUT_LIGHT_DIRECTION:
		{
			glUniform3fv( ParamName, 1, glm::value_ptr( tmpParamPointer->LightDirection ) );
		};
		break;case SHADER_INPUT_AMBLIENT_COLOR:
		{
			glUniform3fv( ParamName, 1, glm::value_ptr( tmpParamPointer->AmblientColor ) );
		};
		break;case SHADER_INPUT_CAMERA_DISTANCE:
		{
			glUniform1f( ParamName, tmpParamPointer->CameraDistance );
		};
		break;case SHADER_INPUT_DIFFUSE_MAP:
		{
			SetTextureToCurrentBlock( ParamName, tmpParamPointer->DiffuseTexture.textureGLid,
			                          tmpParamPointer->DiffuseTexture.textureType );
		};
		break;default:
		{

		}
		break;
	}
}

void
CShaderProgram::SetTextureToCurrentBlock( const  GLint ParamName, const GLint TextureName, const GLenum TextureType )
{
	glActiveTexture( ParamName );
	glBindTexture( TextureType, TextureName );
}

